//***************************************************************************
//*
//*	File:			DialogProgress.cpp
//*	Description:	Dialog for displaying build progress
//*
//***************************************************************************

//
//--------------------------------------------------------------- PRECOMPILED
//

#include "stdafx.h"

//
//--------------------------------------------------- DECLARATION DEPENDENCIES
//

#include "BuildMgr2.h"

//
//--------------------------------------------------------------- DECLARATION
//

#include "DialogProgress.h"

//
//--------------------------------------------------- DEFINITION DEPENDENCIES
//

#include <mapi.h>

//
//-------------------------------------------------------------- PREPROCESSOR
//

#ifdef	_DEBUG
#define new DEBUG_NEW
#undef	THIS_FILE
static	char THIS_FILE[] = __FILE__;
#endif

#pragma	warning ( disable : 4239 )	//	passing CProject to CArray< CProject, CProject& >

//
//---------------------------------------------------------------- DEFINITION
//

//
//---------------------------------------------------------------------------------------------------
//*************************************     CON/DESTRUCTION     *************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																					  Constructor
//***************************************************************************************************
//**	@DOC		CONSTRUCTION
//**	@MFUNC		Default constructor
//**	@PARM		[in|opt] Pointer to the parent window
//**	@END
//***************************************************************************************************
//inline
CDialogProgress::CDialogProgress( CWnd* pParent /*=NULL*/ )
: CDialog( CDialogProgress::IDD, pParent )
{
	//{{AFX_DATA_INIT(CDialogProgress)
	//}}AFX_DATA_INIT

	this->m_pIApplication	= NULL;
	this->m_bPaused			= FALSE;
	this->m_bCancelled		= FALSE;
}

//
//---------------------------------------------------------------------------------------------------
//*****************************************     GET/SET     *****************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																				   SetApplication
//***************************************************************************************************
//**	@DOC		SET
//**	@MFUNC		Sets the pointer to the application's interface
//**	@PARM		[in] Pointer to application's interface
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::SetApplication( IApplication *a_pIApplication )
{
	this->m_pIApplication = a_pIApplication;
}

//
//---------------------------------------------------------------------------------------------------
//***************************************     PERSISTENCE     ***************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																					  LoadProfile 
//***************************************************************************************************
//**	@DOC		PERSISTENCE
//**	@MFUNC		Loads the dialog's settings from the registry
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::LoadProfile()
{
	//
	//	DELETE ALL PROJECTS
	//
	this->m_arrProjects.RemoveAll();

	//
	//	SETUP VARS
	//
	CString strWorkspacePath	;
	CString strWorkspaceName	;
	CString strProjectPath		;
	CString strProjectName		;
	CString	strConfigurations	;
	CString strSection			;
	UINT	iProjects			= 0;
	UINT	iStyle				= 0;
	DWORD	dwCount				= 0;

	///
	//	GET APP
	//
	CWinApp* pApplication =	::AfxGetApp();

	//
	//	READ MAIL SETTINGS
	//
	this->m_bSendMail			= (BOOL)(	pApplication->GetProfileInt		(_T("Configure"), _T("SendMail"				), 0		) )	;
	this->m_strSendTo			=			pApplication->GetProfileString	(_T("Configure"), _T("SendTo"				), _T("")	)	;
	this->m_strStatusLine		=			pApplication->GetProfileString	(_T("Configure"), _T("StatusText"			), _T("")	)	;
	this->m_bMailOnEach			= (BOOL)(	pApplication->GetProfileInt		(_T("Configure"), _T("MailOnEach"			), 1		) )	;
	this->m_bIncBuildResults	= (BOOL)(	pApplication->GetProfileInt		(_T("Configure"), _T("IncludeBuildResults"	), 1		) )	;
	this->m_bSendFile			= (BOOL)(	pApplication->GetProfileInt		(_T("Configure"), _T("SendFile"				), 0		) )	;
	this->m_strFileToSend		=			pApplication->GetProfileString	(_T("Configure"), _T("FileToSend"			)			)	;
	this->m_iFinishedAction		=			pApplication->GetProfileInt		(_T("Configure"), _T("FinishedAction"		), 0		)	;

	//
	//	GET SECTION KEY
	//
	CRegKey	keySection;	
			keySection.Attach( pApplication->GetSectionKey( _T( "Configure" ) ) );

	//
	//	READ PROJECT COUNT
	//
	iProjects = pApplication->GetProfileInt( _T( "Configure" ), _T( "NumProjects" ), 0 );

	//
	//	READ PROJECTS
	//
	for ( UINT iProject = 0; iProject < iProjects; iProject++ )
	{
		//
		//	SETUP SECTION
		//
		strSection.Format( "Project.%04d", iProject );

		//
		//	GET SUB KEY
		//
		CRegKey keyProject	;
				keyProject.Open( keySection, strSection );

		//
		//	READ PROJECT
		//
		dwCount = _MAX_PATH; keyProject.QueryValue( strProjectName		.GetBuffer( _MAX_PATH )	, _T( "0.Project.Name"		), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( strProjectPath		.GetBuffer( _MAX_PATH )	, _T( "1.Project.Path"		), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( strWorkspaceName	.GetBuffer( _MAX_PATH )	, _T( "2.Workspace.Name"	), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( strWorkspacePath	.GetBuffer( _MAX_PATH )	, _T( "3.Workspace.Path"	), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( strConfigurations	.GetBuffer( _MAX_PATH )	, _T( "4.Configurations"	), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( (DWORD&) iStyle								, _T( "5.Style"				) ); 

		strProjectName		.ReleaseBuffer();
		strProjectPath		.ReleaseBuffer();
		strWorkspaceName	.ReleaseBuffer();
		strWorkspacePath	.ReleaseBuffer();
		strConfigurations	.ReleaseBuffer();
		
		//
		//	CLOSE THE KEY
		//
		keyProject.Close();

		//
		//	ADD CONFIGURATIONS
		//
		this->AddProject( strProjectName, strProjectPath, strWorkspaceName, strWorkspacePath, strConfigurations, iStyle );
	}
}

//
//---------------------------------------------------------------------------------------------------
//****************************************     VIRTUALS     *****************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																				   DoDataExchange
//***************************************************************************************************
//**	@DOC		VIRTUALS
//**	@MFUNC		Exchanges data between the dialog's members and its controls
//**	@PARM		[in/out] Pointer to data exchange context
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange( pDX );

	//{{AFX_DATA_MAP(CDialogProgress)
	DDX_Control( pDX, IDC_PROGRESS		, m_ctrlProgress	 );
	DDX_Control( pDX, IDC_PAUSE			, m_ctrlButtonPause	 );
	DDX_Control( pDX, IDC_LIST_PROGRESS	, m_ctrlListProgress );
	//}}AFX_DATA_MAP
}

//
//---------------------------------------------------------------------------------------------------
//****************************************     MESSAGES     *****************************************
//---------------------------------------------------------------------------------------------------
//

BEGIN_MESSAGE_MAP( CDialogProgress, CDialog )
	//{{AFX_MSG_MAP(CDialogProgress)
	ON_BN_CLICKED		( IDC_PAUSE, OnPause )
	ON_WM_SIZE			()
	ON_WM_GETMINMAXINFO	()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BEGIN_EASYSIZE_MAP( CDialogProgress )
	//			CONTROL				LEFT			TOP				RIGHT		BOTTOM	OPTIONS
    EASYSIZE( IDC_PAUSE			, ES_KEEPSIZE	, ES_BORDER		, ES_BORDER	, ES_KEEPSIZE	, 0 )
    EASYSIZE( IDCANCEL			, ES_KEEPSIZE	, ES_BORDER		, ES_BORDER	, ES_KEEPSIZE	, 0 )
    EASYSIZE( IDC_LIST_PROGRESS	, ES_BORDER		, IDC_PAUSE		, IDC_PAUSE	, ES_BORDER		, 0 )
    EASYSIZE( IDC_PROGRESS		, ES_BORDER		, ES_KEEPSIZE	, ES_BORDER	, ES_BORDER		, 0 )
END_EASYSIZE_MAP

//***************************************************************************************************
//**																					 OnInitDialog
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called to initialize the dialog
//**	@END
//***************************************************************************************************
//inline
BOOL CDialogProgress::OnInitDialog() 
{
	//
	//	CALL BASE CLASS' IMPLEMENTATION
	//
	CDialog::OnInitDialog();
	
	//
	//	INIT EASY SIZE
	//
	INIT_EASYSIZE;

	//
	//	SETUP LIST CONTROL'S COLUMNS
	//
	CString strHeader;
	int		iColumn	 ;

	for ( UINT idColumn = IDS_COL_PRG_MIN; idColumn <= IDS_COL_PRG_MAX; idColumn++ ) 
	{
		strHeader.LoadString( idColumn );

		iColumn = idColumn - IDS_COL_PRG_MIN;

		this->m_ctrlListProgress.InsertColumn( iColumn, strHeader, LVCFMT_LEFT, 150, iColumn );
	}
	
	//
	//	MODIFY STYLE
	//
	this->m_ctrlListProgress.SetExtendedStyle( LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES );

	//
	//	LOAD PROFILE
	//
	this->LoadProfile();

	//
	//	RETURN SUCCESS
	//
	return TRUE;  
}

//***************************************************************************************************
//**																						   OnSize
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the dialog's size has changed
//**	@PARM		[in] The type of sizing that has occured
//**	@PARM		[in] The offset along the x axis
//**	@PARM		[in] The offset along the y axis
//**	@COMM		Updates controls' sizes
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::OnSize( UINT nType, int cx, int cy ) 
{
	//
	//	CALL BASE CLASS' IMPLEMENTATION
	//
	CDialog::OnSize(nType, cx, cy);

	//
	//	UPDATE EASYSIZE
	//
	UPDATE_EASYSIZE;
}

//***************************************************************************************************
//**																				  OnGetMinMaxInfo
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called to determine the dialog's min/max size
//**	@PARM		[in] Min/max info
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::OnGetMinMaxInfo( MINMAXINFO FAR* lpMMI ) 
{
	lpMMI->ptMinTrackSize.x = 400;
	lpMMI->ptMinTrackSize.y = 280;

	//
	//	CALL BASE CLASS' IMPLEMENTATION
	//
	CDialog ::OnGetMinMaxInfo(lpMMI);
}

//***************************************************************************************************
//**																						 OnCancel
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the Cancel button is pressed
//**	@COMM		Sets the cancelled flag and resumes the build process in case it has been paused
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::OnCancel() 
{
	//
	//	RESUME
	//
	this->m_bPaused		= TRUE;

	this->OnPause();

	//
	//	CANCEL
	//
	this->m_bCancelled	= TRUE;
}

//***************************************************************************************************
//**																						  OnPause 
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the Pause/Resume button is pressed
//**	@COMM		Toggles the pause flag and sets the button's text accordingly
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::OnPause() 
{
	//
	//	TOGGLE FLAG
	//
	this->m_bPaused = (TRUE == this->m_bPaused) ? FALSE : TRUE;
	
	//
	//	UPDATE BUTTON TEXT
	//
	this->m_ctrlButtonPause.SetWindowText( this->m_bPaused ? _T( "&Resume" ) : _T( "&Pause" ) );

	//
	//	PROCESS MESSAGES
	//
	this->ProcessMessages();
}

//***************************************************************************************************
//**																				  OnBuildFinished		  
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called to signal that DevStudio has finished a build
//**	@PARM		[in] The number of errors
//**	@PARM		[in] The number of warnings
//**	@COMM		Updates the longest-pending project and eventually sends an e-mail notification
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::OnBuildFinished( long a_lErrors, long a_lWarnings )
{
	//
	//	CHECK VALIDITY
	//
	if ( 0 == this->m_arrPending.GetSize() )
	{
		return;		// this happens, when a BuildFinished event "hangs" and the WaitForPending method re-issues the command.
	}

	//
	//	POP PENDING PROJECT [assuming events do not "overtake" each other]
	//
	CProject prj = this->m_arrPending[ 0 ];
	this->m_arrPending.RemoveAt( 0 );

	TRACE3( "[BuildMgr2] %s: %d errors - %d warnings\n", prj.m_strProjectName, a_lErrors, a_lWarnings );

	//
	//	FIND ITEM INDEX
	//
	LVFINDINFO item;
	item.flags	 = LVFI_STRING;
	item.psz	 = prj.m_strProjectName.GetBuffer( _MAX_PATH );

	int iItem = this->m_ctrlListProgress.FindItem( &item, -1 );

	if ( -1 == iItem )
	{
		CString strError; strError.Format( "Could not find item: %s", item.psz );
		this->MessageBox( strError, "CDialogProgress::OnBuildFinished", MB_ICONERROR );
	}

	prj.m_strProjectName.ReleaseBuffer();

	//
	//	SEND NOTIFICATION MAIL
	//
	this->UpdateProject( "Sending notification mail ...", iItem );

	//
	//	SEND NOTIFICATION MAIL
	//
	this->SendMail( prj, a_lErrors, a_lWarnings );

	//
	//	UPDATE STATUS
	//
	this->UpdateProject( (a_lErrors == 0) ? "Completed" : "Failed!", iItem, a_lErrors, a_lWarnings, TRUE );
}

//
//---------------------------------------------------------------------------------------------------
//****************************************     EXECUTION     ****************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																						  Execute
//***************************************************************************************************
//**	@DOC		EXECUTION
//**	@MFUNC		Executes the selected configurations' build process.
//**	@PARM		[in] The name of the configuration to build
//**	@PARM		[in] The build mode (clean/build/rebuild)
//**	@RDESC		<t TRUE> if successful, <t FALSE> otherwise.
//**	@END
//***************************************************************************************************
//inline
BOOL CDialogProgress::Execute( const CString &a_strConfiguration, EBuildMode a_enuMode )
{
	//
	//	RESET
	//
	this->Reset();

	//
	//	STORE CURRENT MODE
	//
	this->m_enuMode = a_enuMode;

	//
	//	SETUP RESULT
	//
	BOOL bResult = TRUE;

	//
	//	CHECK FOR FULL REBUILD
	//
	if ( EBuildMode_Rebuild	== a_enuMode )
	{
		//
		//	EXECUTE CONFIGURATIONS
		//
		this->m_enuMode = EBuildMode_Clean	;	bResult &= this->Execute( a_strConfiguration );	// first, clean all projects
		this->m_enuMode = EBuildMode_Build	;	bResult &= this->Execute( a_strConfiguration );	// then, build all projects
		this->m_enuMode = EBuildMode_Rebuild;													// untimately, restore the flag
	}
	else
	{
		//
		//	EXECUTE CONFIGURATION
		//
		bResult = this->Execute( a_strConfiguration );
	}

	//
	//	SEND NOTIFICATION MAIL FOR ENTIRE CONFIGURATION
	//
	this->SendMail( a_strConfiguration );

	//
	//	RETURN RESULT
	//
	return bResult;
}

//***************************************************************************************************
//**																						  Execute
//***************************************************************************************************
//**	@DOC		EXECUTION
//**	@MFUNC		Executes the selected configurations' build process.
//**	@PARM		[in] The name of the configuration to build
//**	@RDESC		<t TRUE> if successful, <t FALSE> otherwise.
//**	@END
//***************************************************************************************************
//inline
BOOL CDialogProgress::Execute( const CString &a_strConfiguration )
{
	//
	//	CHECK FOR CANCEL
	//
	if ( TRUE == this->m_bCancelled )
	{
		this->m_bCancelled = FALSE;

		return TRUE;
	}

	//
	//	CLEAR PENDING PROJECTS
	//
	this->m_arrPending.RemoveAll();

	//
	//	SETUP RESULT
	//
	BOOL bResult = TRUE;

	//
	//	FOR EACH PROJECT
	//
	for ( int iProject = 0; iProject < this->m_arrProjects.GetSize(); iProject++ )
	{
		//
		//	GET A REFERENCE TO THE PROJECT
		//
		CProject& prj = this->m_arrProjects[ iProject ];

		//
		//	CHECK WHETHER PROJECT IS ACTIVE
		//
		if ( FALSE == prj.m_bEnabled )
		{
			continue;
		}

		//
		//	CHECK WHETHER PROJECT CONTAINS GIVEN CONFIGURATION
		//
		BOOL bContainsConfiguration = FALSE;

		for ( int iConfiguration = 0; iConfiguration < prj.m_arrConfigurations.GetSize(); iConfiguration++ )
		{
			if ( a_strConfiguration == prj.m_arrConfigurations[ iConfiguration ] )
			{
				bContainsConfiguration = TRUE;
				break;
			}
		}

		if ( FALSE == bContainsConfiguration )
		{
			continue;
		}

		//
		//	EXECUTE THAT PROJECT'S CONFIGURATION
		//
		bResult &= this->Execute( prj, iConfiguration );

		//
		//	CHECK FOR CANCEL
		//
		if ( TRUE == this->m_bCancelled )
		{
			break;
		}
	}

	//
	//	WAIT FOR PENDING BUILDS TO FINISH
	//
	this->WaitForPending();

	//
	//	RETURN RESULT
	//
	return bResult;
}

//***************************************************************************************************
//**																						  Execute
//***************************************************************************************************
//**	@DOC		EXECUTION
//**	@MFUNC		Executes the specified configuration within the specified project
//**	@PARM		[in] The project
//**	@PARM		[in] The index of the configuration in the project's array of configurations
//**	@RDESC		<t TRUE> if successful, <t FALSE> otherwise.
//**	@END
//***************************************************************************************************
//inline
BOOL CDialogProgress::Execute( const CProject &a_Project, INT a_iConfiguration )
{
	//
	//	ADD THE PROJECT TO THE STATUS LIST
	//
	this->AddProject	( a_Project, a_iConfiguration );
	this->UpdateProject	( "Opening workspace..." );

	//
	//	LOAD THAT WORKSPACE
	//
	if ( FALSE == this->LoadWorkspace( a_Project.m_strWorkspacePath ) )
	{
		//
		//	UPDATE STATUS
		//
		this->UpdateProject	( "Could not locate workspace..." );

		//
		//	RETURN FAILURE
		//
		return FALSE;
	}

	//
	//	UPDATE STATUS
	//
	this->UpdateProject( "Opened workspace..." );

	//
	//	FIND THAT PROJECT
	//
	CComPtr< IDispatch >	pIDispProjects		;
	CComPtr< IDispatch >	pIDispProject		;
	CComVariant				varProject			; 
	CComVariant				varConfiguration	; 
	CComBSTR				bszProjectPath		;
	CComBSTR				bszProjectName		;
	CComBSTR				bszConfigurationName;
	CString					strConfiguration	;
	long					lProjects			= 0;
	long					lConfigurations		= 0;

	//
	//	GET PROJECTS
	//
	VERIFY_OK( this->m_pIApplication->get_Projects( &pIDispProjects ) );

	CComQIPtr< IProjects, &IID_IProjects > pIProjects( pIDispProjects );

	//
	//	ENUMERATE PROJECTS
	//
	VERIFY_OK( pIProjects->get_Count( &lProjects ) );

	for ( long lProject = 1 ; lProject < lProjects + 1 ; lProject++ )
	{
		//
		//	GET NEXT GENERIC PROJECT
		//
		varProject = lProject;

		CComPtr< IGenericProject > pIGenericProject;	

		VERIFY_OK( pIProjects->Item( varProject, &pIGenericProject ) );

		//
		//	QUERY BUILD PROJECT
		//
		CComQIPtr< IBuildProject, &IID_IBuildProject > pIProject( pIGenericProject );

		//
		//	GET PATH AND NAME
		//
		VERIFY_OK( pIProject->get_FullName	( &bszProjectPath ) );
		VERIFY_OK( pIProject->get_Name		( &bszProjectName ) );

		//
		//	CHECK NAME AND PATH	
		//
		if ( CString( bszProjectName ) != a_Project.m_strProjectName )
		{
			continue;
		}

		if ( CString( bszProjectPath ) != a_Project.m_strProjectPath )
		{
			continue;
		}

		//
		//	UPDATE STATUS
		//
		this->UpdateProject( "Found project..." );

		//
		//	GET CONFIGURATIONS
		//
		CComPtr< IConfigurations > pIConfigurations;

		VERIFY_OK( pIProject->get_Configurations( &pIConfigurations ) );

		//
		//	ENUMERATE CONFIGURATIONS
		//
		VERIFY_OK( pIConfigurations->get_Count( &lConfigurations ) );

		for ( long lConfiguration = 1 ; lConfiguration < lConfigurations + 1 ; lConfiguration++ )
		{
			//
			//	GET NEXT CONFIGURATION
			//
			CComPtr< IConfiguration	> pIConfiguration;

			varConfiguration = lConfiguration;

			VERIFY_OK( pIConfigurations->Item( varConfiguration, &pIConfiguration ) );

			//
			//	GET NAME
			//
			VERIFY_OK( pIConfiguration->get_Name( &bszConfigurationName ) );

			strConfiguration = bszConfigurationName;

			//
			//	REMOVE PROJECT NAME PREFIX
			//
			int iPos = strConfiguration.Find( a_Project.m_strProjectName + " - " );

			if ( 0 == iPos )
			{
				strConfiguration.Delete( 0, a_Project.m_strProjectName.GetLength() + 3 );
			}

			//
			//	CHECK CONFIGURATION
			//
			if ( a_Project.m_arrConfigurations[ a_iConfiguration ] != strConfiguration )
			{
				continue;
			}

			//
			//	UPDATE STATUS
			//
			this->UpdateProject( "Found configuration..." );

			//
			//	BUILD THIS CONFIGURATION
			//
			return this->Execute( a_Project, pIConfiguration );
		}
	}

	//
	//	RETURN FAILURE
	//
	return FALSE;
}

//***************************************************************************************************
//**																						  Execute
//***************************************************************************************************
//**	@DOC		EXECUTION
//**	@MFUNC		Executes the specified configuration of the specified project
//**	@PARM		[in] The project
//**	@PARM		[in] Pointer to the configuration's interface
//**	@RDESC		<t TRUE> if successful, <t FALSE> otherwise.
//**	@END
//***************************************************************************************************
//inline
BOOL CDialogProgress::Execute( const CProject& a_Project, IConfiguration *a_pIConfiguration)
{
	//
	//	CHECK FOR PAUSE
	//
	while( TRUE == this->m_bPaused )
	{
		this->ProcessMessages();
	}

	//
	//	CHECK FOR CANCEL
	//
	if ( TRUE == this->m_bCancelled )
	{
		return TRUE;
	}

	//
	//	SETUP VARS
	//
	CComPtr< IConfiguration > pIConfiguration( a_pIConfiguration );
	CComVariant varDispatch = pIConfiguration;
	HRESULT		hResult		= E_FAIL;

	//
	//	@HACK: COPY THE PROJECT AND STORE THE CONFIGURATION'S INTERFACE
	//
	CProject prjCopy( a_Project );
	prjCopy.m_pIConfiguration = pIConfiguration;

	//
	//	CHECK MODE
	//
	switch ( this->m_enuMode )
	{
									//
									//	CLEAN
									//
		case EBuildMode_Clean	:	this->UpdateProject( "Cleaning..." );
									this->m_arrPending.Add( prjCopy );

									hResult = this->m_pIApplication->Clean( varDispatch );
									break;

									//
									//	BUILD
									//
		case EBuildMode_Build	:	// just build. clean up has already been done
		case EBuildMode_Rebuild	:	this->UpdateProject( "Building..." );
									this->m_arrPending.Add( prjCopy );

									hResult = this->m_pIApplication->Build( varDispatch );
									break;
	}

	//
	//	CHECK RESULT
	//
	if ( FAILED( hResult ) )
	{
		//
		//	POP THIS PROJECT
		//

		//
		//	UPDATE STATUS
		//
		this->UpdateProject( "Error", 0, 0, TRUE );

		//
		//	RETURN FAILURE
		//
		return FALSE;
	}

	//
	//	RETURN SUCCESS
	//
	return TRUE;
}

//
//---------------------------------------------------------------------------------------------------
//*****************************************     HELPERS     *****************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																					   AddProject
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Adds the specified project to the array of known projects
//**	@PARM		[in] The project's name
//**	@PARM		[in] The project's path
//**	@PARM		[in] The workspace's name
//**	@PARM		[in] The workspace's path
//**	@PARM		[in] The project's style flags
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::AddProject( const CString& a_strProjectName, const CString& a_strProjectPath, const CString& a_strWorkspaceName, const CString& a_strWorkspacePath, const CString& a_strConfigurations, int a_iStyle )
{
	//
	//	SETUP PROJECT
	//
	CProject prj;

	//
	//	COPY MEMBERS
	//
	prj.m_strProjectName	= a_strProjectName	;
	prj.m_strProjectPath	= a_strProjectPath	;
	prj.m_strWorkspaceName	= a_strWorkspaceName;
	prj.m_strWorkspacePath	= a_strWorkspacePath;
	prj.m_bEnabled			= (a_iStyle & 0x01)	;

	//
	//	SPLIT CONFIGURATIONS
	//
	CString					strConfigurations( a_strConfigurations );
	CString					strConfiguration ;

	int iPos = strConfigurations.Find( ';' );

	while ( iPos >= 0 )
	{
		//
		//	ADD CONFIGURATION
		//
		prj.m_arrConfigurations.Add( strConfigurations.Left( iPos ) );

		//
		//	REMOVE CONFIGURATION
		//
		strConfigurations = strConfigurations.Right( strConfigurations.GetLength() - iPos - 1 );

		//
		//	FIND NEXT CONFIGURATION
		//
		iPos = strConfigurations.Find( ';' );
	}

	if ( ! strConfigurations.IsEmpty() )
	{
		prj.m_arrConfigurations.Add( strConfigurations );
	}

	//
	//	STORE PROJECT
	//
	this->m_arrProjects.Add( prj );
}

//***************************************************************************************************
//**																					   AddProject
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Adds the specified project's configuration to the progress display
//**	@PARM		[in] The project
//**	@PARM		[in] The configuration's index within the project's list of configurations
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::AddProject( const CProject &a_Project, INT a_iConfiguration )
{
	//
	//	SETUP ITEM
	//
	LVITEM	item	;
	CString strText	;

	item.mask		= LVIF_TEXT;
	item.iItem		= 0;

	//
	//	SUB ITEM 0	(PROJECT)
	//
	strText			= a_Project.m_strProjectName; 
	item.pszText	= strText.GetBuffer( _MAX_PATH );
	item.iSubItem	= 0;

	item.iItem		= this->m_ctrlListProgress.InsertItem( &item );

	strText.ReleaseBuffer();

	//
	//	SUB ITEM 1	(CONFIGURATION)
	//
	strText			= a_Project.m_arrConfigurations[ a_iConfiguration ]; 
	item.pszText	= strText.GetBuffer( _MAX_PATH );
	item.iSubItem	= 1;

					  this->m_ctrlListProgress.SetItem( &item );

	strText.ReleaseBuffer();

	//
	//	ENSURE VISIBILITY
	//
	this->m_ctrlListProgress.EnsureVisible( 0, FALSE );

	//
	//	PROCESS MESSAGES
	//
	this->ProcessMessages();
}

//***************************************************************************************************
//**																					UpdateProject  
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Updates the current project's status
//**	@PARM		[in] The status message
//**	@PARM		[in] The project's item index within the progreess list control
//**				<nl> As long as there is no delay from BuildFinished events, the current project 
//**					 is at index zero.
//**	@PARM		[in] The number of errors.	 Values less than zero will mean "no change".
//**	@PARM		[in] The number of warnings. Values less than zero will mean "no change".
//**	@PARM		[in] A flag indicating whether the project has finished
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::UpdateProject(const CString &a_strStatus, INT a_iIndex, INT a_iErrors, INT a_iWarnings, BOOL a_bFinished)
{
	//
	//	SETUP ITEM
	//
	LVITEM	item	;
	CString strText	;

	item.mask		= LVIF_TEXT;
	item.iItem		= a_iIndex;

	//
	//	SUB ITEM 2	(STATUS)
	//
	strText			= a_strStatus; 
	item.pszText	= strText.GetBuffer( _MAX_PATH );
	item.iSubItem	= 2;

					  this->m_ctrlListProgress.SetItem( &item );

	strText.ReleaseBuffer();

	//
	//	SUB ITEM 3	(ERRORS)
	//
	if ( a_iErrors >= 0 )
	{
		strText			.Format( "%d", a_iErrors );
		item.pszText	= strText.GetBuffer( _MAX_PATH );
		item.iSubItem	= 3;

						  this->m_ctrlListProgress.SetItem( &item );

		strText.ReleaseBuffer();
	}

	//
	//	SUB ITEM 4	(WARNINGS)
	//
	if ( a_iWarnings >= 0 )
	{
		strText			.Format( "%d", a_iWarnings );
		item.pszText	= strText.GetBuffer( _MAX_PATH );
		item.iSubItem	= 4;

						  this->m_ctrlListProgress.SetItem( &item );

		strText.ReleaseBuffer();
	}

	//
	//	CHECK FOR FINISH
	//
	if ( TRUE == a_bFinished )
	{
		this->m_ctrlProgress	.OffsetPos( 1 );
		this->m_ctrlListProgress.SetCheck ( a_iIndex, TRUE );
	}

	//
	//	PROCESS MESSAGES
	//
	this->ProcessMessages();
}

//***************************************************************************************************
//**																							Reset
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Resets the dialog
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::Reset( void )
{
	//
	//	RESET COUNTERS
	//
	this->m_lTotalErrors	= 0;
	this->m_lTotalWarnings	= 0;

	//
	//	RESET FLAGS
	//
//	this->m_bCancelled	= FALSE;
	this->m_bPaused		= FALSE;

	this->m_ctrlButtonPause.SetWindowText( _T("&Pause") );

	//
	//	CLEAR LIST
	//
	this->m_ctrlListProgress.DeleteAllItems();

	//
	//	RESET PROGRESS BAR
	//
	this->m_ctrlProgress.SetRange32	( 0, this->m_arrProjects.GetUpperBound() );
	this->m_ctrlProgress.SetPos		( 0 );

	//
	//	PROCESS MESSAGES
	//
	this->ProcessMessages();
}

//***************************************************************************************************
//**																						 SendMail
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Sends a notification mail when a complete configuration has finished
//**	@PARM		[in] The configuration's name
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::SendMail( const CString& a_strConfiguration )
{
	//
	//	CHECK NECCESSITY
	//
	if ( TRUE == this->m_bMailOnEach )
	{
		return;
	}

	//
	//	TOGGLE FLAG
	//
	this->m_bMailOnEach = TRUE;

	//
	//	SETUP FAKE PROJECT
	//
	CProject prjFake;
	prjFake.m_strProjectName = "All projects' configuration '" +a_strConfiguration+ "'";

	//
	//	SEND MAIL
	//
	this->SendMail( prjFake, this->m_lTotalErrors, this->m_lTotalWarnings );

	//
	//	RESET FLAG
	//
	this->m_bMailOnEach = FALSE;
};

//***************************************************************************************************
//**																						 SendMail
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Sends a notification mail 
//**	@PARM		[in] The project 
//**	@PARM		[in] The number of errors
//**	@PARM		[in] The number of warnings
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::SendMail( const CProject& a_Project, long a_lErrors, long a_lWarnings )
{
	//
	//	TRACK TOTALS
	//
	this->m_lTotalErrors	+= a_lErrors	;
	this->m_lTotalWarnings	+= a_lWarnings	;

	//
	//	CHECK NECCESSITY
	//
	if ( FALSE == this->m_bSendMail ) 
	{
		return;
	}

	if ( FALSE == this->m_bMailOnEach ) 
	{
		return;
	}

	//
	//	LOAD Messaging API
	//
	HINSTANCE hInstMail = ::LoadLibrary( "MAPI32.DLL" );

	if ( NULL == hInstMail )
	{
		return;
	}

	//
	//	GET SendMail FUNCTION
	//
	ULONG (PASCAL *lpfnSendMail)(ULONG, ULONG, MapiMessage*,FLAGS, ULONG);

	(FARPROC&) lpfnSendMail = ::GetProcAddress( hInstMail, "MAPISendMail");

	if ( NULL == lpfnSendMail )
	{
		return;
	}

	//
	//	SETUP MESSAGE
	//
	MapiMessage message			;
	CString		strErrWarnCStr	;
	CString		strToMail		;

	::memset( (void*) &message, 0, sizeof( message ) );

	//
	//	FILL IN SUBJECT
	//
	message.lpszSubject = const_cast<LPTSTR>( (LPCSTR) this->m_strStatusLine );

	if ( TRUE == this->m_bIncBuildResults )
	{
		CString strErrWarnText;
				strErrWarnText.Format( IDS_ERRWARNTEXT, a_Project.m_strProjectName, a_lErrors, a_lWarnings );

		strErrWarnCStr = CString( message.lpszSubject ) + strErrWarnText;

		message.lpszSubject = const_cast<LPTSTR>((LPCSTR)strErrWarnCStr);
	}

	//
	//	FILL IN BODY
	//
	if ( FALSE == this->m_bSendFile )
	{
		strToMail.Format( IDS_DEFAULTMAILBODY, a_Project.m_strProjectName );
	}
	else
	{
		//
		//	READ BODY FROM FILE
		//
		CString		strResult		;
		CStdioFile	fileTextToMail	;

		try
		{
			fileTextToMail.Open(this->m_strFileToSend,	CFile::modeRead | CFile::typeText );

			DWORD	dwFileSize = fileTextToMail.GetLength();
					dwFileSize = fileTextToMail.Read( strResult.GetBuffer( dwFileSize ), dwFileSize );

			strResult.ReleaseBuffer( dwFileSize );

			CString strLocal;
					strLocal.Format( strResult, a_Project.m_strProjectName, a_lErrors, a_lWarnings );

			strToMail = strLocal;
		}
		catch (...) //If some problem occurrs when working
					//  with the file, just send the default
					//  message body
		{
			strToMail.Format( IDS_DEFAULTMAILBODY, a_Project.m_strProjectName );
		}
	}

	//
	//	FILL IN RECIPIENTS
	//
	CString strTemp2 = CString( "SMTP:" ) + this->m_strSendTo;

	message.lpszNoteText			= const_cast<LPTSTR>((LPCSTR)strToMail);
	message.nRecipCount				= 1;
	message.lpRecips				= new MapiRecipDesc;	::memset( message.lpRecips, 0, sizeof( MapiRecipDesc ) );
	message.lpRecips->ulRecipClass	= MAPI_TO;
	message.lpRecips->lpszName		= const_cast<LPTSTR>((LPCSTR)this->m_strSendTo);
	message.lpRecips->lpszAddress	= const_cast<LPTSTR>((LPCSTR)strTemp2);

	//
	//	SEND MAIL
	//
	int iResult = lpfnSendMail( 0, (ULONG) 0, &message, 0, 0 );

	//
	//	DELETE RECIPIENTS
	//
	delete message.lpRecips;

	//
	//	FREE LIBRARY
	//
	::FreeLibrary( hInstMail );
}

//***************************************************************************************************
//**																					LoadWorkspace					  
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Loads the specified workspace
//**	@PARM		[in] The workspace's path
//**	@RDESC		<t TRUE> if successful, <t FALSE> otherwise.
//**	@END
//***************************************************************************************************
//inline
BOOL CDialogProgress::LoadWorkspace( const CString &a_strWorkspacePath )
{
	//
	//	CHECK WHETHER THAT WORKSPACE IS ALREADY ACTIVE
	//
	if ( a_strWorkspacePath == this->m_strActiveWorkspace )
	{
		return TRUE;
	}

	//
	//	CHECK APPLICATION INTERFACE
	//
	if ( NULL == this->m_pIApplication )
	{
		this->MessageBox( "Application interface not intitialized!", "CDialogProgress::LoadWorkspace()", MB_ICONERROR );
		return FALSE;
	}

	//
	//	LOAD THE WORKSPACE
	//
	CComPtr< IDispatch >	pIDispDocuments		;
	CComPtr< IDispatch >	pIDispWorkspace		;  
	CComBSTR				bszWorkspacePathName( a_strWorkspacePath );
	CComVariant				varEmpty			; 

	VERIFY_OK( this->m_pIApplication->get_Documents( &pIDispDocuments ) );

	CComQIPtr< IDocuments, &IID_IDocuments > pIDocuments( pIDispDocuments );

	if ( FAILED( pIDocuments->Open( bszWorkspacePathName, varEmpty, varEmpty, &pIDispWorkspace ) ) )
	{
		return FALSE;
	}

	//
	//	REMEMBER CURRENT WORKSPACE
	//
	this->m_strActiveWorkspace = a_strWorkspacePath;

	//
	//	RETURN SUCCESS
	//
	return TRUE;
}

//***************************************************************************************************
//**																				  ProcessMessages
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Processes pending messages
//**	@PARM		[in] A flag indicating whether to rather process the main application's messages
//**					 than this dialog's messages
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::ProcessMessages( BOOL a_bApp )
{
	//
	//	SETUP VARS
	//
	MSG		msg ;
	HWND	hWnd = this->m_hWnd;

	//
	//	CHECK FOR PENDING MESSAGE
	//
	if ( TRUE == a_bApp )
	{
		CWnd*	pWndMain	= ::AfxGetMainWnd();
		hWnd				= ( NULL != pWndMain ) ? pWndMain->m_hWnd : NULL;
	}

	while ( TRUE == ::PeekMessage( &msg, hWnd, 0, 0, PM_REMOVE ) )
	{
		//
		//	TRANSLATE AND DISPATCH THE MESSAGE
		//
		::TranslateMessage( &msg );
		::DispatchMessage ( &msg );
	}
}

//***************************************************************************************************
//**																				   WaitForPending 
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Waits for pending projects to finish building
//**	@END
//***************************************************************************************************
//inline
void CDialogProgress::WaitForPending( void )
{
	//
	//	UNTIL THE PENDING PROJECTS' QUEUE IS EMPTY
	//
	while ( 0 != this->m_arrPending.GetSize() )
	{
		//
		//	@HACK: Hm, seems trivial, but actually it isn't:
		//

		//
		//	1st try: Process the dialog's message queue until the event comes in
		//
		this->ProcessMessages();		// won't do

		//
		//	2nd try: Process the main app's message queue
		//
	//	this->ProcessMessages( TRUE );	// won't do either

		//
		//	3rd try: Lay the addin's thread to rest for a while
		//
	//	::Sleep( 1000 );				// nor will that suffice

		//
		//	4th try: Do something silly, like issuing the command once again :-(
		//
		CComVariant varDispatch = this->m_arrPending[ this->m_arrPending.GetUpperBound() ].m_pIConfiguration;

		switch ( this->m_enuMode )
		{
										//
										//	CLEAN
										//
			case EBuildMode_Clean	:	this->m_pIApplication->Clean( varDispatch );
										break;

										//
										//	BUILD
										//
			case EBuildMode_Build	:	
			case EBuildMode_Rebuild	:	this->m_pIApplication->Build( varDispatch );
										break;
		}

		//
		//	Well, that works ... but there definitely has to be a better way ...
		//
	}
}